<script>
/**
 * 模态框
 * @Author Mumu 175588516@qq.com
 * @Date 2022/7
 * @Example <modal title="标题" show> 你好 </modal>
 **/
import $cache from '@/common/cache.js';
import list from './modalsExp.js'

export default {
	name:'modal',
	emits: ['update:show','cancel','confirm','canceled'], // 显示状态  关闭事件回调  点击确认回调  已完全关闭回调
	props : {
		show:{
			type	: Boolean,
			default	: false,
		},//显示状态
		title	: {
			type	: String,
			default	: '',
		}, //标题
		titleSlot: {
			type	: String,
			default	: '',
		}, //使用动态插槽title
		showClose	: {
			type	: Boolean,
			default	: true,
		},//显示右上角关闭按钮
		message	: {
			type	: String,
			default	: '',
		}, //内容
		src:{
			type	: String,
			default	: '',
		},//直接打开链接地址 优先级低于message
		footer	: {
			type:Object,
			default(){
				return {
					show	:true, //显示footer
					okText	:'', //确定按钮文字
					okBtnShow	:true, //显示确定按钮
					cancelText	:'', //取消按钮文字
					cancelBtnShow :true, //显示取消按钮
					align:'',//对齐  默认右
				}
			},
		},//footer设置
		footerSlot: {
			type	: String,
			default	: '',
		}, //使用动态插槽处理footer
		size:{
			type	: [String,Object],
			default	: '',
		},//尺寸  fullmax 全屏   默认md中等尺寸 小信息框sm  lg大型框  可接收一个对象用来设置modal-dialog的样式 如宽高  内容超出时 必须设置有高度  建议值100%
		elementOnShow:{
			type	: Boolean,
			default	: true,
		},//仅在显示时加载dom
		applyTo:{
			type	: String,
			default	: '',
		},// dom渲染的节点位置  默认为当前位置 可选如#teleported #app等任意位置 不建议选择body,需要为body的都放入#teleported比较好
		mask	: {
			type	: [Number,Boolean],
			default	: 0.2,
		}, // 遮罩透明度  // false为不使用遮罩 无遮罩时带穿透
		maskClosable:Boolean,//点击遮罩关闭窗口 默认false
		escClosable:{
			type	: Boolean,
			default	: true,
		},//按ESC关闭窗口
		move:{
			type	: Object,
			default() {
				return {
					use		: false, // 是否可移动
					scopy	: false, // 是否容器范围内限制
					reset	: false, // 是否显示时重置位置
				}
			}
		}, //可移动配置
		place:{
			type	: String,
			default	: 'topCenter', // 位置 topLeft topCenter topRight centerLeft center||centerCenter||middle centerRight bottomLeft bottomCenter bottomRight
		}, //位置
		offset		: {
			type	: Object,
			default() {
				return {
					top		: 0, // Y轴偏移
					left	: 0, // X轴偏移
				}
			}
		}, //偏移
		resize		: {
			type	: Array,
			default(){ return [] },
		}, // 是否可调整尺寸 t 上 r右 b下 l左 tl、lt 左上角 tr、rt 右上角 bl、lb 左下角 br、rb右下角 注意：不可向靠边的方向拖出覆盖尺寸，应避免如此使用
		beforeClose:Function, // 关闭前  如果存在该函数 返回为true才会关闭
		showMax:Boolean,// 显示最大化图标
		showMin:Boolean,// 显示最小化图标 慎用   最小化之后页面是可以被操作的   依赖页面数据或依赖页面来驱动的弹窗不建议使用（路由改变后自动销毁）

	},
	mounted(){
		this.toshow(this.show)
		// console.log('mount modal'); //dom方式全都是直接准备好了的
	},
	data() {
		return {
			showmodal:false,
			displayBlock:false,
			zIndex:1050,
			modalStyle:{ left:0, top:0, width:'', height:'', },
			resetoffset:true,
			setMaxMin:'',//设置最大化max或最小化min未设置为空,
			mouseDownActive:false,//鼠标按下拖动状态
		}
	},
	methods:{
		toshow(newval){
			if(this.displayBlock==newval) return;
			if(newval){
				if(this.move.reset || this.resetoffset){
					this.resetoffset=false;
					this.modalStyle.left=(this.offset.left||0)+'px';
					this.modalStyle.top=(this.offset.top||0)+'px';
				}
				if(typeof this.size=='object')
					this.modalStyle={
						...this.modalStyle,
						...this.size,
					}
				this.displayBlock=newval;
				let n = $cache.se('index',null,'_adminModalzIndex')||1050;
				if(this.zIndex<=n){
					this.zIndex=n++
					$cache.se('index',n,'_adminModalzIndex');
				}
				if(!window._modalAllVm) window._modalAllVm=[];
				window._modalAllVm.push(this)
				this.$nextTick(()=>{
					if(this.escClosable) this.$refs.modal.focus();
					setTimeout(()=>{
						this.showmodal=newval;
					},50)
				});
			}else{
				this.showmodal=newval;
				setTimeout(()=>{
					for (let i = 0; i < window._modalAllVm.length; i++){
						if(window._modalAllVm[i]==this) window._modalAllVm.splice(i,1);
					}
					if(window._modalAllVm.length>0){
						let topModal = window._modalAllVm[window._modalAllVm.length-1]
						if(topModal.escClosable && topModal.displayBlock) topModal.$refs.modal.focus();
					}
					this.displayBlock=false;
					this.$emit('canceled');
				},100)
			}
		},//处理show
		close(actionSource){
			if(actionSource=='mask' && !this.maskClosable) return;
			if(actionSource=='keyEsc' && !this.escClosable) return;
			if(actionSource && this.beforeClose && !this.beforeClose(actionSource)) return;
			this.$emit('update:show', false);
			this.$emit('cancel');
			if(this.setMaxMin=='min') this.setMaxMin=''
		},//关闭流程
		moveMouse(el,action,ctr,direction){
			if(!this.move.use && ctr=='move') return;
			if(action=='down'){
				this.mouseDownActive=true;
				this.dialog = this.$refs.modal.querySelector('.modal-dialog');
				if(ctr=='resize'){
					this.ctraction=ctr;
					this.direction=direction;
					this.modalStyle.width = this.dialog.offsetWidth+'px'
					this.modalStyle.height = this.dialog.offsetHeight+'px'
				}
			}else if(action=='up'){
				this.direction=this.ctraction='';
				this.mouseDownActive=false;
			}else if(action=='move'){
				if(!this.mouseDownActive) return;
				el.preventDefault();
				// console.log( this.$refs.modal.offsetWidth, this.$refs.modal.offsetHeight, this.dialog.offsetTop, this.dialog.offsetLeft, this.dialog.offsetWidth, this.dialog.offsetHeight )
				this.ctraction=='resize'?this.serSize(el.movementX,el.movementY):this.moveTo(el.movementX,el.movementY);
			}
		},//拖动事件处理
		moveTo(movementX,movementY){
			if(!!this.move.scopy){
				if((this.dialog.offsetLeft+this.dialog.offsetWidth+movementX>this.$refs.modal.offsetWidth && movementX>0) || (this.dialog.offsetLeft+movementX<=0 && movementX<0)) movementX=0;
				if(this.dialog.offsetTop+this.dialog.offsetHeight+movementY>this.$refs.modal.offsetHeight || this.dialog.offsetTop+movementY<=0) movementY=0;
			}
			this.modalStyle.left=parseFloat(this.modalStyle.left)+movementX+'px'
			this.modalStyle.top=parseFloat(this.modalStyle.top)+movementY+'px'
		},// modal位置变化
		serSize(movementX,movementY){
			let puX=2,puY=2;
			if(this.place.indexOf('top')	>= 0 || this.place.indexOf('bottom')>= 0) puY=1;
			if(this.place.indexOf('top')	>= 0 && this.direction.indexOf('t') >= 0) puY=0;
			if(this.place.indexOf('bottom')	>= 0 && this.direction.indexOf('b') >= 0) puY=0;
			if(this.place.indexOf('Left')	>= 0 || this.place.indexOf('Right') >= 0) puX=1;
			if(this.place.indexOf('Left')	>= 0 && this.direction.indexOf('l') >= 0) puX=0;
			if(this.place.indexOf('Right')	>= 0 && this.direction.indexOf('r') >= 0) puX=0;
			if(['l','r'].indexOf(this.direction) >= 0) movementY=0;
			if(['t','b'].indexOf(this.direction) >= 0) movementX=0;
			if(['tr','rt','r','br','rb'].indexOf(this.direction) >= 0) movementX =  movementX * puX;
			if(['bl','lb','b','br','rb'].indexOf(this.direction) >= 0) movementY =  movementY * puY;
			if(['tl','lt','l','bl','lb'].indexOf(this.direction) >= 0) movementX = -movementX * puX;
			if(['tl','lt','t','tr','rt'].indexOf(this.direction) >= 0) movementY = -movementY * puY;
			this.modalStyle.width = parseFloat(this.modalStyle.width)	+ movementX + 'px'
			this.modalStyle.height= parseFloat(this.modalStyle.height)	+ movementY + 'px'
		},//modal尺寸变化
		toTop(){
			let n = $cache.se('index',null,'_adminModalzIndex')
			if(this.zIndex<=n){
				this.zIndex=n++
				$cache.se('index',n,'_adminModalzIndex');
			}
		},//窗口前置
		setMin(display){
			if(!display){
				list.value.unshift({
					title:this.title,
					recover:this.setMin,
					inst:this,
					close:this.close
				})
			}
			this.displayBlock=display
			this.setMaxMin=display?'':'min'
		},//最小化窗口和恢复
	},
	watch:{
		show:'toshow',
		offset:{
			deep:true,
			handler(newVal,oldVal){
				this.this.resetoffset=true;
			},
		}
	},
	computed:{
		useSize(){
			let style = {}
			if(this.setMaxMin=='max'){
				style.width = '100%'
				style.height = '100%'
				style.margin=0
				style.top=0
				style.left=0
				style['--decrease']=0
			}else if(this.setMaxMin=='min'){
				// style.width = '210px'
				// style.height = '54px'
				// style.margin=0
				// style.position='absolute'
				// style.right='10px'
				// style.bottom='10px'
				// style.top='auto'
				// style.left='auto'
				// 交给组件渲染最小化窗口可更简单处理多个最小化窗口
			}
			return style
		},//计算最大化最小化样式
	}
}
</script>

<template>
	<teleport :to="applyTo || '#teleported'" :disabled="!applyTo">
		<div class="modal fade overflow"
			tabindex="-1" role="dialog"
			ref="modal"
			:class="[{show:displayBlock,in:showmodal},place]"
			:style="{zIndex}"
			@mousemove="moveMouse($event,'move')"
			@mouseup="moveMouse($event,'up')"
			@mouseleave="moveMouse($event,'up')"
			@keyup.esc="close('keyEsc')"
			v-if="elementOnShow?displayBlock:true"
		>
			<div
				class="modal-dialog"
				role="document"
				:class="{full:size=='fullmax','modal-sm':size=='sm','modal-lg':size=='lg'}"
				:style="[modalStyle,useSize]"
				@mousedown="toTop"
			>
				<div class="modal-content overflow flex horizontal" >
					<div class="controlico">
						<button type="button" class="close topclosebtn" data-dismiss="modal" aria-label="Close" @click="close('closebtn')" v-show="showClose" title="关闭窗口">
							<span aria-hidden="true">&times;</span>
						</button>
						<button type="button" class="close topclosebtn" @click="setMaxMin=setMaxMin===''?'max':''" v-if="showMax" :title="setMaxMin===''?'最大化窗口':'还原窗口'">
							<i class="iconfont icon-zuidahua" v-if="setMaxMin===''"></i>
							<i class="iconfont icon-zuidahua1" v-else></i>
						</button>
						<button type="button" class="close topclosebtn" @click="setMin(false)" v-if="showMin && setMaxMin!='min'" title="最小化窗口">
							<i class="iconfont icon-zuixiaohua"></i>
						</button>
					</div>
					
					<div
						class="modal-header"
						:class="{'mp-0':!!titleSlot,cursorMove:move.use}"
						v-if="!!title || !!titleSlot"
						@mousedown="moveMouse($event,'down','move')"
					>
						<template v-if="!!titleSlot">
							<slot :name="titleSlot"></slot>
						</template>
						<h4 class="modal-title" v-else>{{ title }}</h4>
					</div>
					<div class="modal-body flex1 scroll-auto" :class="{'mp-0':!!src}">
						<div v-if="!!message" v-html="message"></div>
						<iframe :src="src" frameborder="0" height="100%" width="100%" v-else-if="!!src" style="display:block"></iframe>
						<template v-else>
							<slot></slot>
						</template>
					</div>
					<div class="modal-footer" :class="[{'mp-0':footerSlot},footer.align]" v-if="footer.show">
						<template v-if="!!footerSlot">
							<slot :name="footerSlot"></slot>
						</template>
						<template v-else>
							<button
								type="button"
								class="btn btn-success"
								:class="{'btn-sm':size=='sm'}"
								@click="$emit('confirm')"
								v-if="footer.okBtnShow!==false"
							>{{ footer.okText||'确定' }}</button>
							<button
								type="button"
								class="btn btn-default "
								:class="{'btn-sm':size=='sm'}"
								data-dismiss="modal"
								@click="close('cancel')"
								v-if="footer.cancelBtnShow!==false"
							>{{ footer.cancelText||'取消' }}</button>
						</template>
					</div>
				</div>
				<div class="setsize" v-for="(v,k) in resize" :key="k" :class="v" @mousedown="moveMouse($event,'down','resize',v)" ></div>
			</div>
			<div class="modal-backdrop fade in" :style="{ opacity : 0 }" v-show="mouseDownActive" v-if="move?.use"><!--兼容需要拖拽但是不显示遮罩又需要操作页面的操作--></div>
			<div class="modal-backdrop fade in" :style="{ opacity : mask }" @click="close('mask')" v-if="mask!==false && setMaxMin!='min'"></div>
		</div>
	</teleport>
</template>

<style lang="less" scoped>
.modal{
	max-height: 100%;
	pointer-events: none;
	&.show{
		display: flex !important;
		&.topLeft,&.centerLeft,&.bottomLeft{
			justify-content:flex-start;
		}
		&.topCenter,&.centerCenter,&.center,&.middle,&.bottomCenter{
			justify-content: center;
		}
		&.topRight,&.centerRight,&.bottomRight{
			justify-content: flex-end;
		}
		&.topLeft,&.topCenter,&.topRight{
			align-items:flex-start;
		}
		&.centerLeft,&.centerCenter,&.center,&.middle,&.centerRight{
			align-items: center;
		}
		&.bottomLeft,&.bottomRight,&.bottomCenter{
			align-items: flex-end;
		}
	}
	.modal-dialog{
		z-index: 2;
		margin: 20px;
		pointer-events: auto;
		min-width: 40px;
		min-height: 40px;
		// max-height: calc(100vh - 60px);
		&.full{
			width: 100%;
			height: 100%;
			max-height: 100%;
			margin: 0;
			--decrease:0;
			// .modal-content{
			// 	max-height: 100vh;
			// }
			.modal-body{
				max-height:none;
				flex: 1;
			}
		}
		&.modal-sm{
			.modal-header{ padding: 5px 10px; }
			.modal-body{ padding: 8px; }
			.modal-footer{ padding: 7px; }
		}
		.setsize{
			// background: red;
			// opacity: .3;
			position: absolute;
			z-index: 9;;
			&.tl,&.lt,&.tr,&.rt,&.bl,&.lb,&.rb,&.br{
				cursor: nw-resize;
				height: 13px;
				width: 13px;
			}
			&.tl,&.lt,&.tr,&.rt{ top: -1px; }
			&.tr,&.rt,&.rb,&.br{ right: -1px; }
			&.bl,&.lb,&.rb,&.br{ bottom: -1px; }
			&.tl,&.lt,&.bl,&.lb{ left: -1px; }
			&.tr,&.rt,&.bl,&.lb{ cursor: ne-resize; }
			&.t,&.b{
				cursor: ns-resize;
				left: 15px;
				right:15px;
				height: 5px;
			}
			&.t{ top: -2px; }
			&.b{ bottom: -2px; }
			&.r,&.l{
				cursor: ew-resize;
				bottom:15px;
				top: 15px;
				width: 5px;
			}
			&.r{ right: -2px; }
			&.l{ left: -2px; }
		}
	}
	.modal-content{
		height: 100%;
		max-height: calc(100vh - var(--decrease,40px));
		border:none;
		.controlico{
			position: absolute;
			top: 0;
			right: 0;
			z-index: 8;
		}
		.topclosebtn{
			padding: 5px 10px;
		}
	}
	.modal-footer{
		.btn{
			min-width: 100px;
		}
		&.left{
			text-align: left;
		}
		&.center{
			text-align: center;
		}
	}
	.modal-backdrop{
		z-index: 1;
		pointer-events: auto;
		&.in{ opacity: .2; }
	}
}
</style>